home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / src / mail / pine3.96.tar.gz / pine3.96.tar / pine3.96 / imap / ANSI / c-client / tenex2.c < prev    next >
C/C++ Source or Header  |  1996-10-15  |  59KB  |  1,857 lines

  1. /*
  2.  * Program:    Tenex mail routines
  3.  *
  4.  * Author:    Mark Crispin
  5.  *        Networks and Distributed Computing
  6.  *        Computing & Communications
  7.  *        University of Washington
  8.  *        Administration Building, AG-44
  9.  *        Seattle, WA  98195
  10.  *        Internet: MRC@CAC.Washington.EDU
  11.  *
  12.  * Date:    22 May 1990
  13.  * Last Edited:    15 October 1996
  14.  *
  15.  * Copyright 1996 by the University of Washington
  16.  *
  17.  *  Permission to use, copy, modify, and distribute this software and its
  18.  * documentation for any purpose and without fee is hereby granted, provided
  19.  * that the above copyright notice appears in all copies and that both the
  20.  * above copyright notice and this permission notice appear in supporting
  21.  * documentation, and that the name of the University of Washington not be
  22.  * used in advertising or publicity pertaining to distribution of the software
  23.  * without specific, written prior permission.  This software is made
  24.  * available "as is", and
  25.  * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
  26.  * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
  27.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
  28.  * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
  29.  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  30.  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
  31.  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
  32.  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  33.  *
  34.  */
  35.  
  36.  
  37. #include <stdio.h>
  38. #include <ctype.h>
  39. #include <errno.h>
  40. extern int errno;        /* just in case */
  41. #include "mail.h"
  42. #include "osdep.h"
  43. #include <pwd.h>
  44. #include <sys/stat.h>
  45. #include <sys/time.h>
  46. #include "tenex2.h"
  47. #include "rfc822.h"
  48. #include "misc.h"
  49. #include "dummy.h"
  50.  
  51. /* Tenex mail routines */
  52.  
  53.  
  54. /* Driver dispatch used by MAIL */
  55.  
  56. DRIVER tenexdriver = {
  57.   "tenex",            /* driver name */
  58.   (DRIVER *) NIL,        /* next driver */
  59.   tenex_valid,            /* mailbox is valid for us */
  60.   tenex_parameters,        /* manipulate parameters */
  61.   tenex_find,            /* find mailboxes */
  62.   tenex_find_bboards,        /* find bboards */
  63.   tenex_find_all,        /* find all mailboxes */
  64.   tenex_find_all_bboards,    /* find all bboards */
  65.   tenex_subscribe,        /* subscribe to mailbox */
  66.   tenex_unsubscribe,        /* unsubscribe from mailbox */
  67.   tenex_subscribe_bboard,    /* subscribe to bboard */
  68.   tenex_unsubscribe_bboard,    /* unsubscribe from bboard */
  69.   tenex_create,            /* create mailbox */
  70.   tenex_delete,            /* delete mailbox */
  71.   tenex_rename,            /* rename mailbox */
  72.   tenex_open,            /* open mailbox */
  73.   tenex_close,            /* close mailbox */
  74.   tenex_fetchfast,        /* fetch message "fast" attributes */
  75.   tenex_fetchflags,        /* fetch message flags */
  76.   tenex_fetchstructure,        /* fetch message envelopes */
  77.   tenex_fetchheader,        /* fetch message header only */
  78.   tenex_fetchtext,        /* fetch message body only */
  79.   tenex_fetchbody,        /* fetch message body section */
  80.   tenex_setflag,        /* set message flag */
  81.   tenex_clearflag,        /* clear message flag */
  82.   tenex_search,            /* search for message based on criteria */
  83.   tenex_ping,            /* ping mailbox to see if still alive */
  84.   tenex_check,            /* check for new messages */
  85.   tenex_expunge,        /* expunge deleted messages */
  86.   tenex_copy,            /* copy messages to another mailbox */
  87.   tenex_move,            /* move messages to another mailbox */
  88.   tenex_append,            /* append string message to mailbox */
  89.   tenex_gc            /* garbage collect stream */
  90. };
  91.  
  92.                 /* prototype stream */
  93. MAILSTREAM tenexproto = {&tenexdriver};
  94.  
  95. /* Tenex mail validate mailbox
  96.  * Accepts: mailbox name
  97.  * Returns: our driver if name is valid, NIL otherwise
  98.  */
  99.  
  100. DRIVER *tenex_valid (char *name)
  101. {
  102.   char tmp[MAILTMPLEN];
  103.   return tenex_isvalid (name,tmp) ? &tenexdriver : NIL;
  104. }
  105.  
  106.  
  107. /* Tenex mail test for valid mailbox
  108.  * Accepts: mailbox name
  109.  * Returns: T if valid, NIL otherwise
  110.  */
  111.  
  112. int tenex_isvalid (char *name,char *tmp)
  113. {
  114.   int fd;
  115.   int ret = NIL;
  116.   char *s,file[MAILTMPLEN];
  117.   struct stat sbuf;
  118.   time_t tp[2];
  119.   errno = EINVAL;        /* assume invalid argument */
  120.                 /* if file, get its status */
  121.   if ((*name != '{') && !((*name == '*') && (name[1] == '{')) &&
  122.       (s = tenex_file (file,name)) && !stat (s,&sbuf)) {
  123.     if (!sbuf.st_size) {    /* allow empty file if INBOX */
  124.       if ((s = mailboxfile (tmp,name)) && !*s) ret = T;
  125.       else errno = 0;        /* empty file */
  126.     }
  127.     else if ((fd = open (file,O_RDONLY,NIL)) >= 0) {
  128.       memset (tmp,'\0',MAILTMPLEN);
  129.       if ((read (fd,tmp,64) >= 0) && (s = strchr (tmp,'\012')) &&
  130.       (s[-1] != '\015')) {    /* valid format? */
  131.     *s = '\0';        /* tie off header */
  132.                 /* must begin with dd-mmm-yy" */
  133.     ret = (((tmp[2] == '-' && tmp[6] == '-') ||
  134.         (tmp[1] == '-' && tmp[5] == '-')) &&
  135.            (s = strchr (tmp+20,',')) && strchr (s+2,';')) ? T : NIL;
  136.       }
  137.       else errno = -1;        /* bogus format */
  138.       close (fd);        /* close the file */
  139.       tp[0] = sbuf.st_atime;    /* preserve atime and mtime */
  140.       tp[1] = sbuf.st_mtime;
  141.       utime (file,tp);        /* set the times */
  142.     }
  143.   }
  144.   return ret;            /* return what we should */
  145. }
  146.  
  147.  
  148. /* Tenex manipulate driver parameters
  149.  * Accepts: function code
  150.  *        function-dependent value
  151.  * Returns: function-dependent return value
  152.  */
  153.  
  154. void *tenex_parameters (long function,void *value)
  155. {
  156.   return NIL;
  157. }
  158.  
  159. /* Tenex mail find list of mailboxes
  160.  * Accepts: mail stream
  161.  *        pattern to search
  162.  */
  163.  
  164. void tenex_find (MAILSTREAM *stream,char *pat)
  165. {
  166.   if (stream) dummy_find (NIL,pat);
  167. }
  168.  
  169.  
  170. /* Tenex mail find list of bboards
  171.  * Accepts: mail stream
  172.  *        pattern to search
  173.  */
  174.  
  175. void tenex_find_bboards (MAILSTREAM *stream,char *pat)
  176. {
  177.   if (stream) dummy_find_bboards (NIL,pat);
  178. }
  179.  
  180.  
  181. /* Tenex mail find list of all mailboxes
  182.  * Accepts: mail stream
  183.  *        pattern to search
  184.  */
  185.  
  186. void tenex_find_all (MAILSTREAM *stream,char *pat)
  187. {
  188.   if (stream) dummy_find_all (NIL,pat);
  189. }
  190.  
  191.  
  192. /* Tenex mail find list of all bboards
  193.  * Accepts: mail stream
  194.  *        pattern to search
  195.  */
  196.  
  197. void tenex_find_all_bboards (MAILSTREAM *stream,char *pat)
  198. {
  199.   if (stream) dummy_find_all_bboards (NIL,pat);
  200. }
  201.  
  202. /* Tenex mail subscribe to mailbox
  203.  * Accepts: mail stream
  204.  *        mailbox to add to subscription list
  205.  * Returns: T on success, NIL on failure
  206.  */
  207.  
  208. long tenex_subscribe (MAILSTREAM *stream,char *mailbox)
  209. {
  210.   char tmp[MAILTMPLEN];
  211.   return sm_subscribe (tenex_file (tmp,mailbox));
  212. }
  213.  
  214.  
  215. /* Tenex mail unsubscribe to mailbox
  216.  * Accepts: mail stream
  217.  *        mailbox to delete from subscription list
  218.  * Returns: T on success, NIL on failure
  219.  */
  220.  
  221. long tenex_unsubscribe (MAILSTREAM *stream,char *mailbox)
  222. {
  223.   char tmp[MAILTMPLEN];
  224.   return sm_unsubscribe (tenex_file (tmp,mailbox));
  225. }
  226.  
  227.  
  228. /* Tenex mail subscribe to bboard
  229.  * Accepts: mail stream
  230.  *        bboard to add to subscription list
  231.  * Returns: T on success, NIL on failure
  232.  */
  233.  
  234. long tenex_subscribe_bboard (MAILSTREAM *stream,char *mailbox)
  235. {
  236.   return NIL;            /* never valid for Tenex */
  237. }
  238.  
  239.  
  240. /* Tenex mail unsubscribe to bboard
  241.  * Accepts: mail stream
  242.  *        bboard to delete from subscription list
  243.  * Returns: T on success, NIL on failure
  244.  */
  245.  
  246. long tenex_unsubscribe_bboard (MAILSTREAM *stream,char *mailbox)
  247. {
  248.   return NIL;            /* never valid for Tenex */
  249. }
  250.  
  251. /* Tenex mail create mailbox
  252.  * Accepts: MAIL stream
  253.  *        mailbox name to create
  254.  * Returns: T on success, NIL on failure
  255.  */
  256.  
  257. long tenex_create (MAILSTREAM *stream,char *mailbox)
  258. {
  259.   return dummy_create (stream,mailbox);
  260. }
  261.  
  262.  
  263. /* Tenex mail delete mailbox
  264.  * Accepts: MAIL stream
  265.  *        mailbox name to delete
  266.  * Returns: T on success, NIL on failure
  267.  */
  268.  
  269. long tenex_delete (MAILSTREAM *stream,char *mailbox)
  270. {
  271.   return tenex_rename (stream,mailbox,NIL);
  272. }
  273.  
  274. /* Tenex mail rename mailbox
  275.  * Accepts: MAIL stream
  276.  *        old mailbox name
  277.  *        new mailbox name (or NIL for delete)
  278.  * Returns: T on success, NIL on failure
  279.  */
  280.  
  281. long tenex_rename (MAILSTREAM *stream,char *old,char *new)
  282. {
  283.   long ret = T;
  284.   char tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
  285.   int ld;
  286.   int fd = open (tenex_file (file,old),O_RDWR,NIL);
  287.                 /* lock out non c-client applications */
  288.   if (fd < 0) {            /* open mailbox */
  289.     sprintf (tmp,"Can't open mailbox %s: %s",old,strerror (errno));
  290.     mm_log (tmp,ERROR);
  291.     return NIL;
  292.   }
  293.                 /* get exclusive parse/append permission */
  294.   if ((ld = tenex_lock (fd,lock,LOCK_EX)) < 0) {
  295.     mm_log ("Unable to lock rename mailbox",ERROR);
  296.     return NIL;
  297.   }
  298.                 /* lock out other users */
  299.   if (flock (fd,LOCK_EX|LOCK_NB)) {
  300.     close (fd);            /* couldn't lock, give up on it then */
  301.     sprintf (tmp,"Mailbox %s is in use by another process",old);
  302.     mm_log (tmp,ERROR);
  303.     tenex_unlock (ld,lock);    /* release exclusive parse/append permission */
  304.     return NIL;
  305.   }
  306.                 /* do the rename or delete operation */
  307.   if (new ? rename (file,tenex_file (tmp,new)) : unlink (file)) {
  308.     sprintf (tmp,"Can't %s mailbox %s: %s",new ? "rename" : "delete",old,
  309.          strerror (errno));
  310.     mm_log (tmp,ERROR);
  311.     ret = NIL;            /* set failure */
  312.   }
  313.   flock (fd,LOCK_UN);        /* release lock on the file */
  314.   tenex_unlock (ld,lock);    /* release exclusive parse/append permission */
  315.   close (fd);            /* close the file */
  316.   return ret;            /* return success */
  317. }
  318.  
  319. /* Tenex mail open
  320.  * Accepts: stream to open
  321.  * Returns: stream on success, NIL on failure
  322.  */
  323.  
  324. MAILSTREAM *tenex_open (MAILSTREAM *stream)
  325. {
  326.   int fd,ld;
  327.   char tmp[MAILTMPLEN];
  328.                 /* return prototype for OP_PROTOTYPE call */
  329.   if (!stream) return user_flags (&tenexproto);
  330.   if (LOCAL) {            /* close old file if stream being recycled */
  331.     tenex_close (stream);    /* dump and save the changes */
  332.     stream->dtb = &tenexdriver;    /* reattach this driver */
  333.     mail_free_cache (stream);    /* clean up cache */
  334.   }
  335.   user_flags (stream);        /* set up user flags */
  336.                 /* force readonly if bboard */
  337.   if (*stream->mailbox == '*') stream->rdonly = T;
  338.   if (stream->rdonly ||
  339.       (fd = open (tenex_file (tmp,stream->mailbox),O_RDWR,NIL)) < 0) {
  340.     if ((fd = open (tenex_file (tmp,stream->mailbox),O_RDONLY,NIL)) < 0) {
  341.       sprintf (tmp,"Can't open mailbox: %s",strerror (errno));
  342.       mm_log (tmp,ERROR);
  343.       return NIL;
  344.     }
  345.     else if (!stream->rdonly) {    /* got it, but readonly */
  346.       mm_log ("Can't get write access to mailbox, access is readonly",WARN);
  347.       stream->rdonly = T;
  348.     }
  349.   }
  350.   stream->local = fs_get (sizeof (TENEXLOCAL));
  351.   LOCAL->buf = (char *) fs_get (MAXMESSAGESIZE + 1);
  352.   LOCAL->buflen = MAXMESSAGESIZE;
  353.   LOCAL->hdr = LOCAL->txt = NIL;/* no cached data yet */
  354.   LOCAL->hdrmsgno = LOCAL->txtmsgno = 0;
  355.                 /* note if an INBOX or not */
  356.   LOCAL->inbox = !strcmp (ucase (strcpy (LOCAL->buf,stream->mailbox)),"INBOX");
  357.   if (*stream->mailbox != '*') {/* canonicalize the stream mailbox name */
  358.     fs_give ((void **) &stream->mailbox);
  359.     stream->mailbox = cpystr (tmp);
  360.   }
  361.                 /* get shared parse permission */
  362.   if ((ld = tenex_lock (fd,tmp,LOCK_SH)) < 0) {
  363.     mm_log ("Unable to lock open mailbox",ERROR);
  364.     return NIL;
  365.   }
  366.   flock(LOCAL->fd = fd,LOCK_SH);/* bind and lock the file */
  367.   tenex_unlock (ld,tmp);    /* release shared parse permission */
  368.   LOCAL->filesize = 0;        /* initialize parsed file size */
  369.   LOCAL->filetime = 0;        /* time not set up yet */
  370.   LOCAL->mustcheck = LOCAL->shouldcheck = NIL;
  371.   stream->sequence++;        /* bump sequence number */
  372.                 /* parse mailbox */
  373.   stream->nmsgs = stream->recent = 0;
  374.   if (tenex_ping (stream) && !stream->nmsgs)
  375.     mm_log ("Mailbox is empty",(long) NIL);
  376.   if (!LOCAL) return NIL;    /* failure if stream died */
  377.   return stream;        /* return stream to caller */
  378. }
  379.  
  380. /* Tenex mail close
  381.  * Accepts: MAIL stream
  382.  */
  383.  
  384. void tenex_close (MAILSTREAM *stream)
  385. {
  386.   if (stream && LOCAL) {    /* only if a file is open */
  387.     flock (LOCAL->fd,LOCK_UN);    /* unlock local file */
  388.     close (LOCAL->fd);        /* close the local file */
  389.                 /* free local text buffer */
  390.     if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
  391.     if (LOCAL->hdr) fs_give ((void **) &LOCAL->hdr);
  392.     if (LOCAL->txt) fs_give ((void **) &LOCAL->txt);
  393.                 /* nuke the local data */
  394.     fs_give ((void **) &stream->local);
  395.     stream->dtb = NIL;        /* log out the DTB */
  396.   }
  397. }
  398.  
  399.  
  400. /* Tenex mail fetch fast information
  401.  * Accepts: MAIL stream
  402.  *        sequence
  403.  */
  404.  
  405. void tenex_fetchfast (MAILSTREAM *stream,char *sequence)
  406. {
  407.   long i;
  408.                 /* make sure have RFC-822 size for messages */
  409.   if (stream && LOCAL && mail_sequence (stream,sequence))
  410.     for (i = 1; i <= stream->nmsgs; i++)
  411.       if (mail_elt (stream,i)->sequence) tenex_822size (stream,i);
  412. }
  413.  
  414.  
  415. /* Tenex mail fetch flags
  416.  * Accepts: MAIL stream
  417.  *        sequence
  418.  */
  419.  
  420. void tenex_fetchflags (MAILSTREAM *stream,char *sequence)
  421. {
  422.   long i;
  423.                 /* ping mailbox, get new status for messages */
  424.   if (tenex_ping (stream) && mail_sequence (stream,sequence))
  425.     for (i = 1; i <= stream->nmsgs; i++) 
  426.       if (mail_elt (stream,i)->sequence) tenex_elt (stream,i);
  427. }
  428.  
  429. /* Tenex mail fetch structure
  430.  * Accepts: MAIL stream
  431.  *        message # to fetch
  432.  *        pointer to return body
  433.  * Returns: envelope of this message, body returned in body value
  434.  *
  435.  * Fetches the "fast" information as well
  436.  */
  437.  
  438. ENVELOPE *tenex_fetchstructure (MAILSTREAM *stream,long msgno,BODY **body)
  439. {
  440.   LONGCACHE *lelt;
  441.   ENVELOPE **env;
  442.   BODY **b;
  443.   STRING bs;
  444.   unsigned long i,hdrsize,textsize;
  445.   tenex_822size (stream,msgno);    /* make sure we have message size */
  446.   if (stream->scache) {        /* short cache */
  447.     if (msgno != stream->msgno){/* flush old poop if a different message */
  448.       mail_free_envelope (&stream->env);
  449.       mail_free_body (&stream->body);
  450.     }
  451.     stream->msgno = msgno;
  452.     env = &stream->env;        /* get pointers to envelope and body */
  453.     b = &stream->body;
  454.   }
  455.   else {            /* long cache */
  456.     lelt = mail_lelt (stream,msgno);
  457.     env = &lelt->env;        /* get pointers to envelope and body */
  458.     b = &lelt->body;
  459.   }
  460.   if ((body && !*b) || !*env) {    /* have the poop we need? */
  461.     mail_free_envelope (env);    /* flush old envelope and body */
  462.     mail_free_body (b);
  463.                 /* read the header */
  464.     tenex_fetchheader_work (stream,msgno,&hdrsize);
  465.     if (body) {            /* don't bother with text if don't want body */
  466.       tenex_fetchtext_work (stream,msgno,&textsize);
  467.       INIT (&bs,mail_string,(void *) LOCAL->txt,textsize);
  468.     }
  469.     else textsize = 0;        /* no text then */
  470.                 /* make sure enough space */
  471.     if ((i = max (hdrsize,textsize)) > LOCAL->buflen) {
  472.       fs_give ((void **) &LOCAL->buf);
  473.       LOCAL->buf = (char *) fs_get ((LOCAL->buflen = i) + 1);
  474.     }
  475.                 /* parse envelope and body */
  476.     rfc822_parse_msg (env,body ? b : NIL,LOCAL->hdr,hdrsize,body ? &bs : NIL,
  477.               mylocalhost (),LOCAL->buf);
  478.   }
  479.   if (body) *body = *b;        /* return the body */
  480.   return *env;            /* return the envelope */
  481. }
  482.  
  483. /* Tenex mail fetch message header
  484.  * Accepts: MAIL stream
  485.  *        message # to fetch
  486.  * Returns: message header in RFC822 format
  487.  */
  488.  
  489. char *tenex_fetchheader (MAILSTREAM *stream,long msgno)
  490. {
  491.   unsigned long hdrsize;
  492.   char *hdr = tenex_fetchheader_work (stream,msgno,&hdrsize);
  493.                 /* copy the string */
  494.   strcrlfcpy (&LOCAL->buf,&LOCAL->buflen,hdr,hdrsize);
  495.   return LOCAL->buf;
  496. }
  497.  
  498.  
  499. /* Tenex mail fetch message header work
  500.  * Accepts: MAIL stream
  501.  *        message # to fetch
  502.  *        pointer to size to return
  503.  * Returns: message header in RFC822 format
  504.  */
  505.  
  506. char *tenex_fetchheader_work (MAILSTREAM *stream,long m,unsigned long *siz)
  507. {
  508.   unsigned long pos = tenex_header (stream,m,siz);
  509.   if (LOCAL->hdrmsgno != m) {    /* is cache correct? */
  510.     if (LOCAL->hdr) fs_give ((void **) &LOCAL->hdr);
  511.     LOCAL->hdr = (char *) fs_get (1 + *siz);
  512.     LOCAL->hdr[*siz] = '\0';    /* tie off string */
  513.     LOCAL->hdrmsgno = m;    /* note cache */
  514.     lseek (LOCAL->fd,pos,L_SET);/* get to header position */
  515.                 /* slurp the data */
  516.     read (LOCAL->fd,LOCAL->hdr,*siz);
  517.   }
  518.   return LOCAL->hdr;
  519. }
  520.  
  521. /* Tenex mail fetch message text (body only)
  522.  * Accepts: MAIL stream
  523.  *        message # to fetch
  524.  * Returns: message text in RFC822 format
  525.  */
  526.  
  527. char *tenex_fetchtext (MAILSTREAM *stream,long msgno)
  528. {
  529.   unsigned long txtsize;
  530.   char *txt = tenex_fetchtext_work (stream,msgno,&txtsize);
  531.                 /* mark message as seen */
  532.   tenex_elt (stream,msgno)->seen = T;
  533.                 /* recalculate status */
  534.   tenex_update_status (stream,msgno,T);
  535.                 /* copy the string */
  536.   strcrlfcpy (&LOCAL->buf,&LOCAL->buflen,txt,txtsize);
  537.   return LOCAL->buf;
  538. }
  539.  
  540.  
  541. /* Tenex mail fetch message header work
  542.  * Accepts: MAIL stream
  543.  *        message # to fetch
  544.  *        pointer to size to return
  545.  * Returns: message header in RFC822 format
  546.  */
  547.  
  548. char *tenex_fetchtext_work (MAILSTREAM *stream,long m,unsigned long *siz)
  549. {
  550.   unsigned long hdrsize;
  551.   unsigned long pos = tenex_header (stream,m,&hdrsize);
  552.   *siz = tenex_size (stream,m) - hdrsize;
  553.   if (LOCAL->txtmsgno != m) {    /* is cache correct? */
  554.     if (LOCAL->txt) fs_give ((void **) &LOCAL->txt);
  555.     LOCAL->txt = (char *) fs_get (1 + *siz);
  556.     LOCAL->txt[*siz] = '\0';    /* tie off string */
  557.     LOCAL->txtmsgno = m;    /* note cache */
  558.                 /* get to text position */
  559.     lseek (LOCAL->fd,pos + hdrsize,L_SET);
  560.                 /* slurp the data */
  561.     read (LOCAL->fd,LOCAL->txt,*siz);
  562.   }
  563.   return LOCAL->txt;
  564. }
  565.  
  566. /* Tenex fetch message body as a structure
  567.  * Accepts: Mail stream
  568.  *        message # to fetch
  569.  *        section specifier
  570.  *        pointer to length
  571.  * Returns: pointer to section of message body
  572.  */
  573.  
  574. char *tenex_fetchbody (MAILSTREAM *stream,long m,char *s,unsigned long *len)
  575. {
  576.   BODY *b;
  577.   PART *pt;
  578.   char *t;
  579.   unsigned long i;
  580.   unsigned long base;
  581.   unsigned long offset = 0;
  582.   unsigned long hdrpos = tenex_header (stream,m,&base);
  583.   MESSAGECACHE *elt = tenex_elt (stream,m);
  584.                 /* make sure have a body */
  585.   if (!(tenex_fetchstructure (stream,m,&b) && b && s && *s &&
  586.     ((i = strtol (s,&s,10)) > 0))) return NIL;
  587.   do {                /* until find desired body part */
  588.                 /* multipart content? */
  589.     if (b->type == TYPEMULTIPART) {
  590.       pt = b->contents.part;    /* yes, find desired part */
  591.       while (--i && (pt = pt->next));
  592.       if (!pt) return NIL;    /* bad specifier */
  593.                 /* note new body, check valid nesting */
  594.       if (((b = &pt->body)->type == TYPEMULTIPART) && !*s) return NIL;
  595.       offset = pt->offset;    /* get new offset */
  596.     }
  597.     else if (i != 1) return NIL;/* otherwise must be section 1 */
  598.                 /* need to go down further? */
  599.     if (i = *s) switch (b->type) {
  600.     case TYPEMESSAGE:        /* embedded message, calculate new base */
  601.       offset = b->contents.msg.offset;
  602.       b = b->contents.msg.body;    /* get its body, drop into multipart case */
  603.     case TYPEMULTIPART:        /* multipart, get next section */
  604.       if ((*s++ == '.') && (i = strtol (s,&s,10)) > 0) break;
  605.     default:            /* bogus subpart specification */
  606.       return NIL;
  607.     }
  608.   } while (i);
  609.                 /* lose if body bogus */
  610.   if ((!b) || b->type == TYPEMULTIPART) return NIL;
  611.   elt->seen = T;        /* mark message as seen */
  612.                 /* recalculate status */
  613.   tenex_update_status (stream,m,T);
  614.                 /* move to that place in the data */
  615.   lseek (LOCAL->fd,hdrpos + base + offset,L_SET);
  616.                 /* slurp the data */
  617.   t = (char *) fs_get (1 + b->size.ibytes);
  618.   read (LOCAL->fd,t,b->size.ibytes);
  619.   rfc822_contents(&LOCAL->buf,&LOCAL->buflen,len,t,b->size.ibytes,b->encoding);
  620.   fs_give ((void **) &t);    /* flush readin buffer */
  621.   return LOCAL->buf;
  622. }
  623.  
  624. /* Tenex locate header for a message
  625.  * Accepts: MAIL stream
  626.  *        message number
  627.  *        pointer to returned header size
  628.  * Returns: position of header in file
  629.  */
  630.  
  631. unsigned long tenex_header (MAILSTREAM *stream,long msgno,unsigned long *size)
  632. {
  633.   long siz;
  634.   long i = 0;
  635.   char c = '\0';
  636.   char *s = NIL;
  637.   MESSAGECACHE *elt = tenex_elt (stream,msgno);
  638.   long pos = elt->data1 + (elt->data2 >> 24);
  639.   long msiz = tenex_size (stream,msgno);
  640.                 /* is size known? */
  641.   if (!(*size = (elt->data2 & (unsigned long) 0xffffff))) {
  642.     lseek (LOCAL->fd,pos,L_SET);/* get to header position */
  643.                 /* search message for LF LF */
  644.     for (siz = 0; siz < msiz; siz++) {
  645.       if (--i <= 0)        /* read another buffer as necessary */
  646.     read (LOCAL->fd,s = LOCAL->buf,i = min (msiz-siz,(long) MAILTMPLEN));
  647.                 /* two newline sequence? */
  648.       if ((c == '\012') && (*s == '\012')) {
  649.                 /* yes, note for later */
  650.     elt->data2 |= (*size = siz + 1);
  651.     return pos;        /* return to caller */
  652.       }
  653.       else c = *s++;        /* next character */
  654.     }
  655.   }
  656.   return pos;            /* have position */
  657. }
  658.  
  659. /* Tenex mail set flag
  660.  * Accepts: MAIL stream
  661.  *        sequence
  662.  *        flag(s)
  663.  */
  664.  
  665. void tenex_setflag (MAILSTREAM *stream,char *sequence,char *flag)
  666. {
  667.   MESSAGECACHE *elt;
  668.   long i;
  669.   unsigned long uf;
  670.   short f;
  671.   struct stat sbuf;
  672.   if (LOCAL->filetime && !LOCAL->shouldcheck) {
  673.     fstat (LOCAL->fd,&sbuf);    /* get current write time */
  674.     if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T;
  675.   }
  676.                 /* no-op if no flags to modify */
  677.   if (!((f = tenex_getflags (stream,flag,&uf)) || uf)) return;
  678.                 /* get sequence and loop on it */
  679.   if (mail_sequence (stream,sequence)) for (i = 1; i <= stream->nmsgs; i++)
  680.     if ((elt = mail_elt (stream,i))->sequence) {
  681.       tenex_elt (stream,i);    /* set all requested flags */
  682.       if (f&fSEEN) elt->seen = T;
  683.       if (f&fDELETED) elt->deleted = T;
  684.       if (f&fFLAGGED) elt->flagged = T;
  685.       if (f&fANSWERED) elt->answered = T;
  686.       elt->user_flags |= uf;
  687.                 /* recalculate status */
  688.       tenex_update_status (stream,i,NIL);
  689.     }
  690.   if (!stream->rdonly) {    /* make sure the update takes */
  691.     fsync (LOCAL->fd);
  692.     fstat (LOCAL->fd,&sbuf);    /* get current write time */
  693.     LOCAL->filetime = sbuf.st_mtime;
  694.   }
  695. }
  696.  
  697. /* Tenex mail clear flag
  698.  * Accepts: MAIL stream
  699.  *        sequence
  700.  *        flag(s)
  701.  */
  702.  
  703. void tenex_clearflag (MAILSTREAM *stream,char *sequence,char *flag)
  704. {
  705.   MESSAGECACHE *elt;
  706.   long i;
  707.   unsigned long uf;
  708.   short f;
  709.   struct stat sbuf;
  710.   if (LOCAL->filetime && !LOCAL->shouldcheck) {
  711.     fstat (LOCAL->fd,&sbuf);    /* get current write time */
  712.     if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T;
  713.   }
  714.                 /* no-op if no flags to modify */
  715.   if (!((f = tenex_getflags (stream,flag,&uf)) || uf)) return;
  716.                 /* get sequence and loop on it */
  717.   if (mail_sequence (stream,sequence)) for (i = 1; i <= stream->nmsgs; i++)
  718.     if ((elt = mail_elt (stream,i))->sequence) {
  719.       tenex_elt (stream,i);    /* clear all requested flags */
  720.       if (f&fSEEN) elt->seen = NIL;
  721.       if (f&fDELETED) elt->deleted = NIL;
  722.       if (f&fFLAGGED) elt->flagged = NIL;
  723.       if (f&fANSWERED) elt->answered = NIL;
  724.       elt->user_flags &= ~uf;
  725.                 /* recalculate status */
  726.       tenex_update_status (stream,i,NIL);
  727.     }
  728.   if (!stream->rdonly) {    /* make sure the update takes */
  729.     fsync (LOCAL->fd);
  730.     fstat (LOCAL->fd,&sbuf);    /* get current write time */
  731.     LOCAL->filetime = sbuf.st_mtime;
  732.   }
  733. }
  734.  
  735. /* Tenex mail search for messages
  736.  * Accepts: MAIL stream
  737.  *        search criteria
  738.  */
  739.  
  740. void tenex_search (MAILSTREAM *stream,char *criteria)
  741. {
  742.   long i,n;
  743.   char *d;
  744.   search_t f;
  745.                 /* initially all searched */
  746.   for (i = 1; i <= stream->nmsgs; ++i) mail_elt (stream,i)->searched = T;
  747.                 /* get first criterion */
  748.   if (criteria && (criteria = strtok (criteria," "))) {
  749.                 /* for each criterion */
  750.     for (; criteria; (criteria = strtok (NIL," "))) {
  751.       f = NIL; d = NIL; n = 0;    /* init then scan the criterion */
  752.       switch (*ucase (criteria)) {
  753.       case 'A':            /* possible ALL, ANSWERED */
  754.     if (!strcmp (criteria+1,"LL")) f = tenex_search_all;
  755.     else if (!strcmp (criteria+1,"NSWERED")) f = tenex_search_answered;
  756.     break;
  757.       case 'B':            /* possible BCC, BEFORE, BODY */
  758.     if (!strcmp (criteria+1,"CC"))
  759.       f = tenex_search_string (tenex_search_bcc,&d,&n);
  760.     else if (!strcmp (criteria+1,"EFORE"))
  761.       f = tenex_search_date (tenex_search_before,&n);
  762.     else if (!strcmp (criteria+1,"ODY"))
  763.       f = tenex_search_string (tenex_search_body,&d,&n);
  764.     break;
  765.       case 'C':            /* possible CC */
  766.     if (!strcmp (criteria+1,"C")) 
  767.       f = tenex_search_string (tenex_search_cc,&d,&n);
  768.     break;
  769.       case 'D':            /* possible DELETED */
  770.     if (!strcmp (criteria+1,"ELETED")) f = tenex_search_deleted;
  771.     break;
  772.       case 'F':            /* possible FLAGGED, FROM */
  773.     if (!strcmp (criteria+1,"LAGGED")) f = tenex_search_flagged;
  774.     else if (!strcmp (criteria+1,"ROM"))
  775.       f = tenex_search_string (tenex_search_from,&d,&n);
  776.     break;
  777.       case 'K':            /* possible KEYWORD */
  778.     if (!strcmp (criteria+1,"EYWORD"))
  779.       f = tenex_search_flag (tenex_search_keyword,&n,stream);
  780.     break;
  781.       case 'N':            /* possible NEW */
  782.     if (!strcmp (criteria+1,"EW")) f = tenex_search_new;
  783.     break;
  784.  
  785.       case 'O':            /* possible OLD, ON */
  786.     if (!strcmp (criteria+1,"LD")) f = tenex_search_old;
  787.     else if (!strcmp (criteria+1,"N"))
  788.       f = tenex_search_date (tenex_search_on,&n);
  789.     break;
  790.       case 'R':            /* possible RECENT */
  791.     if (!strcmp (criteria+1,"ECENT")) f = tenex_search_recent;
  792.     break;
  793.       case 'S':            /* possible SEEN, SINCE, SUBJECT */
  794.     if (!strcmp (criteria+1,"EEN")) f = tenex_search_seen;
  795.     else if (!strcmp (criteria+1,"INCE"))
  796.       f = tenex_search_date (tenex_search_since,&n);
  797.     else if (!strcmp (criteria+1,"UBJECT"))
  798.       f = tenex_search_string (tenex_search_subject,&d,&n);
  799.     break;
  800.       case 'T':            /* possible TEXT, TO */
  801.     if (!strcmp (criteria+1,"EXT"))
  802.       f = tenex_search_string (tenex_search_text,&d,&n);
  803.     else if (!strcmp (criteria+1,"O"))
  804.       f = tenex_search_string (tenex_search_to,&d,&n);
  805.     break;
  806.       case 'U':            /* possible UN* */
  807.     if (criteria[1] == 'N') {
  808.       if (!strcmp (criteria+2,"ANSWERED")) f = tenex_search_unanswered;
  809.       else if (!strcmp (criteria+2,"DELETED")) f = tenex_search_undeleted;
  810.       else if (!strcmp (criteria+2,"FLAGGED")) f = tenex_search_unflagged;
  811.       else if (!strcmp (criteria+2,"KEYWORD"))
  812.         f = tenex_search_flag (tenex_search_unkeyword,&n,stream);
  813.       else if (!strcmp (criteria+2,"SEEN")) f = tenex_search_unseen;
  814.     }
  815.     break;
  816.       default:            /* we will barf below */
  817.     break;
  818.       }
  819.       if (!f) {            /* if can't determine any criteria */
  820.     sprintf (LOCAL->buf,"Unknown search criterion: %.80s",criteria);
  821.     mm_log (LOCAL->buf,ERROR);
  822.     return;
  823.       }
  824.                 /* run the search criterion */
  825.       for (i = 1; i <= stream->nmsgs; ++i)
  826.     if (mail_elt (stream,i)->searched && !(*f) (stream,i,d,n))
  827.       mail_elt (stream,i)->searched = NIL;
  828.     }
  829.                 /* report search results to main program */
  830.     for (i = 1; i <= stream->nmsgs; ++i)
  831.       if (mail_elt (stream,i)->searched) mail_searched (stream,i);
  832.   }
  833. }
  834.  
  835. /* Tenex mail ping mailbox
  836.  * Accepts: MAIL stream
  837.  * Returns: T if stream still alive, NIL if not
  838.  */
  839.  
  840. long tenex_ping (MAILSTREAM *stream)
  841. {
  842.   long i = 1;
  843.   long r = NIL;
  844.   int ld;
  845.   char lock[MAILTMPLEN];
  846.   struct stat sbuf;
  847.   if (stream && LOCAL) {    /* only if stream already open */
  848.     if (LOCAL->filetime && !(LOCAL->mustcheck || LOCAL->shouldcheck)) {
  849.       fstat (LOCAL->fd,&sbuf);    /* get current write time */
  850.       if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T;
  851.     }
  852.                 /* check for changed message status */
  853.     if (LOCAL->mustcheck || LOCAL->shouldcheck) {
  854.       if (LOCAL->shouldcheck)    /* babble when we do this unilaterally */
  855.     mm_notify (stream,"[CHECK] Checking for flag updates",NIL);
  856.       while (i <= stream->nmsgs) tenex_elt (stream,i++);
  857.       LOCAL->mustcheck = LOCAL->shouldcheck = NIL;
  858.     }
  859.                 /* get shared parse/append permission */
  860.     if ((ld = tenex_lock (LOCAL->fd,lock,LOCK_SH)) >= 0) {
  861.                 /* parse resulting mailbox */
  862.       r = (tenex_parse (stream)) ? T : NIL;
  863.       tenex_unlock (ld,lock);    /* release shared parse/append permission */
  864.     }
  865.                 /* snarf if this is a read-write inbox */
  866.     if (stream && LOCAL && LOCAL->inbox && !stream->rdonly) {
  867.       tenex_snarf (stream);
  868.                 /* get shared parse/append permission */
  869.       if ((ld = tenex_lock (LOCAL->fd,lock,LOCK_SH)) >= 0) {
  870.                 /* parse resulting mailbox */
  871.     r = (tenex_parse (stream)) ? T : NIL;
  872.     tenex_unlock (ld,lock);    /* release shared parse/append permission */
  873.       }
  874.     }
  875.   }
  876.   return r;            /* return result of the parse */
  877. }
  878.  
  879.  
  880. /* Tenex mail check mailbox (reparses status too)
  881.  * Accepts: MAIL stream
  882.  */
  883.  
  884. void tenex_check (MAILSTREAM *stream)
  885. {
  886.                 /* mark that a check is desired */
  887.   if (LOCAL) LOCAL->mustcheck = T;
  888.   if (tenex_ping (stream)) mm_log ("Check completed",(long) NIL);
  889. }
  890.  
  891. /* Tenex mail snarf messages from bezerk inbox
  892.  * Accepts: MAIL stream
  893.  */
  894.  
  895. void tenex_snarf (MAILSTREAM *stream)
  896. {
  897.   long i = 0;
  898.   long r,j;
  899.   struct stat sbuf;
  900.   struct iovec iov[2];
  901.   char lock[MAILTMPLEN];
  902.   MESSAGECACHE *elt;
  903.   MAILSTREAM *bezerk = NIL;
  904.   int ld;
  905.                 /* give up if can't get exclusive permission */
  906.   if ((!strcmp (sysinbox (),stream->mailbox)) ||
  907.       ((ld = tenex_lock (LOCAL->fd,lock,LOCK_EX)) < 0)) return;
  908.   mm_critical (stream);        /* go critical */
  909.   stat (sysinbox (),&sbuf);    /* see if anything there */
  910.   if (sbuf.st_size) {        /* non-empty? */
  911.     fstat (LOCAL->fd,&sbuf);    /* yes, get current file size */
  912.                 /* sizes match and can get bezerk mailbox? */
  913.     if ((sbuf.st_size == LOCAL->filesize) && 
  914.     (bezerk = mail_open (bezerk,sysinbox (),OP_SILENT)) &&
  915.     (!bezerk->rdonly) && (r = bezerk->nmsgs)) {
  916.                 /* yes, go to end of file in our mailbox */
  917.       lseek (LOCAL->fd,sbuf.st_size,L_SET);
  918.                 /* for each message in bezerk mailbox */
  919.       while (r && (++i <= bezerk->nmsgs)) {
  920.                 /* snarf message from Berkeley mailbox */
  921.     iov[1].iov_base = bezerk_snarf (bezerk,i,&j);
  922.                 /* calculate header line */
  923.     mail_date ((iov[0].iov_base = LOCAL->buf),elt = mail_elt (bezerk,i));
  924.     sprintf (LOCAL->buf + strlen (LOCAL->buf),",%ld;0000000000%02o\n",
  925.          iov[1].iov_len = j,(fOLD * !(elt->recent)) +
  926.          (fSEEN * elt->seen) + (fDELETED * elt->deleted) +
  927.          (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered));
  928.     iov[0].iov_len = strlen (iov[0].iov_base);
  929.                 /* copy message to new mailbox */
  930.     if ((writev (LOCAL->fd,iov,2) < 0) || fsync (LOCAL->fd)) {
  931.       sprintf (LOCAL->buf,"Can't copy new mail: %s",strerror (errno));
  932.       mm_log (LOCAL->buf,ERROR);
  933.       ftruncate (LOCAL->fd,sbuf.st_size);
  934.       r = 0;        /* flag that we have lost big */
  935.     }
  936.       }
  937.       fstat (LOCAL->fd,&sbuf);    /* yes, get current file size */
  938.       LOCAL->filetime = sbuf.st_mtime;
  939.       if (r) {            /* delete all the messages we copied */
  940.     for (i = 1; i <= r; i++) mail_elt (bezerk,i)->deleted = T;
  941.     mail_expunge (bezerk);    /* now expunge all those messages */
  942.       }
  943.     }
  944.     if (bezerk) mail_close (bezerk);
  945.   }
  946.   mm_nocritical (stream);    /* release critical */
  947.   tenex_unlock (ld,lock);    /* release exclusive parse/append permission */
  948. }
  949.  
  950. /* Tenex mail expunge mailbox
  951.  * Accepts: MAIL stream
  952.  */
  953.  
  954. void tenex_expunge (MAILSTREAM *stream)
  955. {
  956.   struct stat sbuf;
  957.   off_t pos = 0;
  958.   int ld;
  959.   unsigned long i = 1;
  960.   unsigned long j,k,m,recent;
  961.   unsigned long n = 0;
  962.   unsigned long delta = 0;
  963.   char lock[MAILTMPLEN];
  964.   MESSAGECACHE *elt;
  965.                 /* do nothing if stream dead */
  966.   if (!tenex_ping (stream)) return;
  967.   if (stream->rdonly) {        /* won't do on readonly files! */
  968.     mm_log ("Expunge ignored on readonly mailbox",WARN);
  969.     return;
  970.   }
  971.   if (LOCAL->filetime && !LOCAL->shouldcheck) {
  972.     fstat (LOCAL->fd,&sbuf);    /* get current write time */
  973.     if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T;
  974.   }
  975.   /* The cretins who designed flock() created a window of vulnerability in
  976.    * upgrading locks from shared to exclusive or downgrading from exclusive
  977.    * to shared.  Rather than maintain the lock at shared status at a minimum,
  978.    * flock() actually *releases* the former lock.  Obviously they never talked
  979.    * to any database guys.  Fortunately, we have the parse/append permission
  980.    * lock.  If we require this lock before going exclusive on the mailbox,
  981.    * another process can not sneak in and steal the exclusive mailbox lock on
  982.    * us, because it will block on trying to get parse/append permission first.
  983.    */
  984.                 /* get exclusive parse/append permission */
  985.   if ((ld = tenex_lock (LOCAL->fd,lock,LOCK_EX)) < 0) {
  986.     mm_log ("Unable to lock expunge mailbox",ERROR);
  987.     return;
  988.   }
  989.                 /* get exclusive access */
  990.   if (flock (LOCAL->fd,LOCK_EX|LOCK_NB)) {
  991.     flock (LOCAL->fd,LOCK_SH);    /* recover previous lock */
  992.     mm_log("Can't expunge because mailbox is in use by another process",ERROR);
  993.     tenex_unlock (ld,lock);    /* release exclusive parse/append permission */
  994.     return;
  995.   }
  996.   tenex_gc (stream,GC_TEXTS);    /* flush texts */
  997.  
  998.   mm_critical (stream);        /* go critical */
  999.   recent = stream->recent;    /* get recent now that pinged and locked */
  1000.   while (i <= stream->nmsgs) {    /* for each message */
  1001.                 /* number of bytes to smash or preserve */
  1002.     k = ((elt = tenex_elt (stream,i))->data2 >> 24) + tenex_size (stream,i);
  1003.     if (elt->deleted) {        /* if deleted */
  1004.       if (elt->recent) --recent;/* if recent, note one less recent message */
  1005.       delta += k;        /* number of bytes to delete */
  1006.       mail_expunged (stream,i);    /* notify upper levels */
  1007.       n++;            /* count up one more expunged message */
  1008.     }
  1009.     else if (i++ && delta) {    /* preserved message */
  1010.       j = elt->data1;        /* first byte to preserve */
  1011.       do {            /* read from source position */
  1012.     m = min (k,LOCAL->buflen);
  1013.     lseek (LOCAL->fd,j,L_SET);
  1014.     read (LOCAL->fd,LOCAL->buf,(unsigned int) m);
  1015.     pos = j - delta;    /* write to destination position */
  1016.     lseek (LOCAL->fd,pos,L_SET);
  1017.     write (LOCAL->fd,LOCAL->buf,(unsigned int) m);
  1018.     pos += m;        /* new position */
  1019.     j += m;            /* next chunk, perhaps */
  1020.       } while (k -= m);        /* until done */
  1021.       elt->data1 -= delta;    /* note the new address of this text */
  1022.     }
  1023.     else pos = elt->data1 + k;    /* preserved but no deleted messages */
  1024.   }
  1025.   if (n) {            /* truncate file after last message */
  1026.     if (pos != (LOCAL->filesize -= delta)) {
  1027.       sprintf (LOCAL->buf,"Calculated size mismatch %ld != %ld, delta = %ld",
  1028.            pos,LOCAL->filesize,delta);
  1029.       mm_log (LOCAL->buf,WARN);
  1030.       LOCAL->filesize = pos;    /* fix it then */
  1031.     }
  1032.     ftruncate (LOCAL->fd,LOCAL->filesize);
  1033.     sprintf (LOCAL->buf,"Expunged %ld messages",n);
  1034.                 /* output the news */
  1035.     mm_log (LOCAL->buf,(long) NIL);
  1036.   }
  1037.   else mm_log ("No messages deleted, so no update needed",(long) NIL);
  1038.   fsync (LOCAL->fd);        /* force disk update */
  1039.   fstat (LOCAL->fd,&sbuf);    /* get new write time */
  1040.   LOCAL->filetime = sbuf.st_mtime;
  1041.   mm_nocritical (stream);    /* release critical */
  1042.                 /* notify upper level of new mailbox size */
  1043.   mail_exists (stream,stream->nmsgs);
  1044.   mail_recent (stream,recent);
  1045.   flock (LOCAL->fd,LOCK_SH);    /* allow sharers again */
  1046.   tenex_unlock (ld,lock);    /* release exclusive parse/append permission */
  1047. }
  1048.  
  1049. /* Tenex mail copy message(s)
  1050.  * Accepts: MAIL stream
  1051.  *        sequence
  1052.  *        destination mailbox
  1053.  * Returns: T if success, NIL if failed
  1054.  */
  1055.  
  1056. long tenex_copy (MAILSTREAM *stream,char *sequence,char *mailbox)
  1057. {
  1058.                 /* copy the messages */
  1059.   return (mail_sequence (stream,sequence)) ?
  1060.     tenex_copy_messages (stream,mailbox) : NIL;
  1061. }
  1062.  
  1063.  
  1064. /* Tenex mail move message(s)
  1065.  * Accepts: MAIL stream
  1066.  *        sequence
  1067.  *        destination mailbox
  1068.  * Returns: T if success, NIL if failed
  1069.  */
  1070.  
  1071. long tenex_move (MAILSTREAM *stream,char *sequence,char *mailbox)
  1072. {
  1073.   long i;
  1074.   struct stat sbuf;
  1075.   MESSAGECACHE *elt;
  1076.   if (!(mail_sequence (stream,sequence) &&
  1077.     tenex_copy_messages (stream,mailbox))) return NIL;
  1078.                 /* delete all requested messages */
  1079.   for (i = 1; i <= stream->nmsgs; i++)
  1080.     if ((elt = tenex_elt (stream,i))->sequence) {
  1081.       elt->deleted = T;        /* mark message deleted */
  1082.                 /* recalculate status */
  1083.       tenex_update_status (stream,i,NIL);
  1084.     }
  1085.   if (!stream->rdonly) {    /* make sure the update takes */
  1086.     fsync (LOCAL->fd);
  1087.     fstat (LOCAL->fd,&sbuf);    /* get current write time */
  1088.     LOCAL->filetime = sbuf.st_mtime;
  1089.   }
  1090.   return LONGT;
  1091. }
  1092.  
  1093. /* Tenex mail append message from stringstruct
  1094.  * Accepts: MAIL stream
  1095.  *        destination mailbox
  1096.  *        stringstruct of messages to append
  1097.  * Returns: T if append successful, else NIL
  1098.  */
  1099.  
  1100. long tenex_append (MAILSTREAM *stream,char *mailbox,char *flags,char *date,
  1101.            STRING *message)
  1102. {
  1103.   struct stat sbuf;
  1104.   int fd,ld;
  1105.   char c,*s,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
  1106.   time_t tp[2];
  1107.   MESSAGECACHE elt;
  1108.   long i = SIZE (message);
  1109.   long size = 0;
  1110.   long ret = LONGT;
  1111.   unsigned long uf = 0;
  1112.   short f = 0;
  1113.   if (flags) {            /* get flags if given */
  1114.     unsigned long ruf;
  1115.     f = tenex_getflags (user_flags (&tenexproto),flags,&ruf);
  1116.                 /* reverse bits (dontcha wish we had CIRC?) */
  1117.     while (ruf) uf |= 1 << (29 - find_rightmost_bit (&ruf));
  1118.   }
  1119.   if (date) {            /* want to preserve date? */
  1120.                 /* yes, parse date into an elt */
  1121.     if (!mail_parse_date (&elt,date)) {
  1122.       sprintf (tmp,"Bad date in append: %s",date);
  1123.       mm_log (tmp,ERROR);
  1124.       return NIL;
  1125.     }
  1126.   }
  1127.                 /* N.B.: can't use LOCAL->buf for tmp */
  1128.                 /* make sure valid mailbox */
  1129.   if (!tenex_isvalid (mailbox,tmp)) switch (errno) {
  1130.   case ENOENT:            /* no such file? */
  1131.     mm_notify (stream,"[TRYCREATE] Must create mailbox before append",NIL);
  1132.     return NIL;
  1133.   case 0:            /* merely empty file? */
  1134.     break;
  1135.   case EINVAL:
  1136.     sprintf (tmp,"Invalid Tenex-format mailbox name: %s",mailbox);
  1137.     mm_log (tmp,ERROR);
  1138.     return NIL;
  1139.   default:
  1140.     sprintf (tmp,"Not a Tenex-format mailbox: %s",mailbox);
  1141.     mm_log (tmp,ERROR);
  1142.     return NIL;
  1143.   }
  1144.   if ((fd = open (tenex_file (file,mailbox),O_RDWR|O_CREAT,
  1145.           S_IREAD|S_IWRITE)) < 0) {
  1146.     sprintf (tmp,"Can't open append mailbox: %s",strerror (errno));
  1147.     mm_log (tmp,ERROR);
  1148.     return NIL;
  1149.   }
  1150.                 /* get exclusive parse/append permission */
  1151.   if ((ld = tenex_lock (fd,lock,LOCK_EX)) < 0) {
  1152.     mm_log ("Unable to lock append mailbox",ERROR);
  1153.     return NIL;
  1154.   }
  1155.   s = (char *) fs_get (i + 1);    /* get space for the data */
  1156.                 /* copy the data w/o CR's */
  1157.   while (i--) if ((c = SNX (message)) != '\015') s[size++] = c;
  1158.  
  1159.   mm_critical (stream);        /* go critical */
  1160.   fstat (fd,&sbuf);        /* get current file size */
  1161.   lseek (fd,sbuf.st_size,L_SET);/* move to end of file */
  1162.                 /* write preseved date */
  1163.   if (date) mail_date (tmp,&elt);
  1164.   else internal_date (tmp);    /* get current date in IMAP format */
  1165.                 /* add remainder of header */
  1166.   sprintf (tmp+26,",%ld;%010lo%02o\n",size,uf,f);
  1167.                 /* write header */
  1168.   if ((write (fd,tmp,strlen (tmp)) < 0) || ((write (fd,s,size)) < 0) ||
  1169.       fsync (fd)) {
  1170.     sprintf (tmp,"Message append failed: %s",strerror (errno));
  1171.     mm_log (tmp,ERROR);
  1172.     ftruncate (fd,sbuf.st_size);
  1173.     ret = NIL;
  1174.   }
  1175.   tp[0] = sbuf.st_atime;    /* preserve atime and mtime */
  1176.   tp[1] = sbuf.st_mtime;
  1177.   utime (file,tp);        /* set the times */
  1178.   tenex_unlock (ld,lock);    /* release exclusive parse/append permission */
  1179.   close (fd);            /* close the file */
  1180.   mm_nocritical (stream);    /* release critical */
  1181.   fs_give ((void **) &s);    /* flush the buffer */
  1182.   return ret;
  1183. }
  1184.  
  1185. /* Tenex garbage collect stream
  1186.  * Accepts: Mail stream
  1187.  *        garbage collection flags
  1188.  */
  1189.  
  1190. void tenex_gc (MAILSTREAM *stream,long gcflags)
  1191. {
  1192.   if (gcflags & GC_TEXTS) {    /* flush texts */
  1193.     LOCAL->hdrmsgno = LOCAL->txtmsgno = 0;
  1194.     if (LOCAL->hdr) fs_give ((void **) &LOCAL->hdr);
  1195.     if (LOCAL->txt) fs_give ((void **) &LOCAL->txt);
  1196.   }
  1197. }
  1198.  
  1199. /* Internal routines */
  1200.  
  1201.  
  1202. /* Tenex mail lock file for parse/append permission
  1203.  * Accepts: file descriptor
  1204.  *        lock file name buffer
  1205.  *        type of locking operation (LOCK_SH or LOCK_EX)
  1206.  * Returns: file descriptor of lock or -1 if failure
  1207.  */
  1208.  
  1209. int tenex_lock (int fd,char *lock,int op)
  1210. {
  1211.   int ld;
  1212.   struct stat sbuf;
  1213.   if (!fstat (fd,&sbuf)) {    /* get data for this file */
  1214.                 /* make temporary file name */
  1215.     sprintf (lock,"/tmp/.%hx.%lx",sbuf.st_dev,sbuf.st_ino);
  1216.     if (chk_notsymlink (lock,T) &&
  1217.     ((ld = open (lock,O_RDWR|O_CREAT,
  1218.              (int)mail_parameters(NIL,GET_LOCKPROTECTION,NIL))) >= 0)){
  1219.       flock (ld,op);        /* get this lock */
  1220.       return ld;        /* return locking file descriptor */
  1221.     }
  1222.   }
  1223.   return -1;
  1224. }
  1225.  
  1226.  
  1227. /* Tenex mail unlock file for parse/append permission
  1228.  * Accepts: file descriptor
  1229.  *        lock file name from tenex_lock()
  1230.  */
  1231.  
  1232. void tenex_unlock (int fd,char *lock)
  1233. {
  1234.   unlink (lock);        /* delete the file */
  1235.   flock (fd,LOCK_UN);        /* unlock it */
  1236.   close (fd);            /* close it */
  1237. }
  1238.  
  1239. /* Tenex mail return internal message size in bytes
  1240.  * Accepts: MAIL stream
  1241.  *        message #
  1242.  * Returns: internal size of message
  1243.  */
  1244.  
  1245. unsigned long tenex_size (MAILSTREAM *stream,long m)
  1246. {
  1247.   MESSAGECACHE *elt = mail_elt (stream,m);
  1248.   return ((m < stream->nmsgs) ? mail_elt (stream,m+1)->data1 : LOCAL->filesize)
  1249.     - (elt->data1 + (elt->data2 >> 24));
  1250. }
  1251.  
  1252.  
  1253. /* Tenex mail return RFC-822 size in bytes
  1254.  * Accepts: MAIL stream
  1255.  *        message #
  1256.  * Returns: message size
  1257.  */
  1258.  
  1259. unsigned long tenex_822size (MAILSTREAM *stream,long msgno)
  1260. {
  1261.   char *s;
  1262.   unsigned long i;
  1263.   STRING bs;
  1264.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1265.   if (!elt->rfc822_size) {    /* have header size yet? */
  1266.                 /* get size including CR's  */
  1267.     s = tenex_fetchheader_work (stream,msgno,&i);
  1268.     INIT (&bs,mail_string,(void *) s,i);
  1269.     elt->rfc822_size = strcrlflen (&bs);
  1270.     s = tenex_fetchtext_work (stream,msgno,&i);
  1271.     INIT (&bs,mail_string,(void *) s,i);
  1272.     elt->rfc822_size += strcrlflen (&bs); 
  1273.   }
  1274.   return elt->rfc822_size;
  1275. }
  1276.  
  1277.  
  1278. /* Tenex mail generate file string
  1279.  * Accepts: temporary buffer to write into
  1280.  *        mailbox name string
  1281.  * Returns: local file string or NIL if failure
  1282.  */
  1283.  
  1284. char *tenex_file (char *dst,char *name)
  1285. {
  1286.   char tmp[MAILTMPLEN];
  1287.   char *s = mailboxfile (dst,name);
  1288.                 /* return our standard inbox */
  1289.   return (s && !*s) ? mailboxfile (dst,tenex_isvalid ("~/mail.TxT",tmp) ?
  1290.                    "mail.TxT" : "mail.txt") : s;
  1291. }
  1292.  
  1293. /* Parse flag list
  1294.  * Accepts: MAIL stream
  1295.  *        flag list as a character string
  1296.  *        pointer to user flags to return
  1297.  * Returns: system flags
  1298.  */
  1299.  
  1300. long tenex_getflags (MAILSTREAM *stream,char *flag,unsigned long *uf)
  1301. {
  1302.   char key[MAILTMPLEN];
  1303.   char *t,*s,tmp[MAILTMPLEN];
  1304.   short f = 0;
  1305.   long i;
  1306.   short j;
  1307.   *uf = 0;            /* initially no user flags */
  1308.   if (flag && *flag) {        /* no-op if no flag string */
  1309.                 /* check if a list and make sure valid */
  1310.     if ((i = (*flag == '(')) ^ (flag[strlen (flag)-1] == ')')) {
  1311.       mm_log ("Bad flag list",ERROR);
  1312.       return NIL;
  1313.     }
  1314.                 /* copy the flag string w/o list construct */
  1315.     strncpy (tmp,flag+i,(j = strlen (flag) - (2*i)));
  1316.     tmp[j] = '\0';        /* tie off tail */
  1317.                 /* make uppercase, find first, parse */
  1318.     if (t = strtok (ucase (tmp)," ")) do {
  1319.       i = 0;            /* no flag yet */
  1320.                 /* system flag, dispatch on first character */
  1321.       if (*t == '\\') switch (*++t) {
  1322.       case 'S':            /* possible \Seen flag */
  1323.     if (t[1] == 'E' && t[2] == 'E' && t[3] == 'N' && t[4] == '\0')
  1324.       f |= i = fSEEN;
  1325.     break;
  1326.       case 'D':            /* possible \Deleted flag */
  1327.     if (t[1] == 'E' && t[2] == 'L' && t[3] == 'E' && t[4] == 'T' &&
  1328.         t[5] == 'E' && t[6] == 'D' && t[7] == '\0') f |= i = fDELETED;
  1329.     break;
  1330.       case 'F':            /* possible \Flagged flag */
  1331.     if (t[1] == 'L' && t[2] == 'A' && t[3] == 'G' && t[4] == 'G' &&
  1332.         t[5] == 'E' && t[6] == 'D' && t[7] == '\0') f |= i = fFLAGGED;
  1333.     break;
  1334.       case 'A':            /* possible \Answered flag */
  1335.     if (t[1] == 'N' && t[2] == 'S' && t[3] == 'W' && t[4] == 'E' &&
  1336.         t[5] == 'R' && t[6] == 'E' && t[7] == 'D' && t[8] == '\0')
  1337.       f |= i = fANSWERED;
  1338.     break;
  1339.       default:            /* unknown */
  1340.     break;
  1341.       }
  1342.  
  1343.                 /* user flag, search through table */
  1344.       else for (j = 0; !i && j < NUSERFLAGS && (s =stream->user_flags[j]); ++j)
  1345.     if (!strcmp (t,ucase (strcpy (key,s)))) *uf |= i = 1 << j;
  1346.       if (!i) {            /* didn't find a matching flag? */
  1347.     sprintf (key,"Unknown flag: %.80s",t);
  1348.     mm_log (key,ERROR);
  1349.       }
  1350.                 /* parse next flag */
  1351.     } while (t = strtok (NIL," "));
  1352.   }
  1353.   return f;
  1354. }
  1355.  
  1356. /* Tenex mail parse mailbox
  1357.  * Accepts: MAIL stream
  1358.  * Returns: T if parse OK
  1359.  *        NIL if failure, stream aborted
  1360.  */
  1361.  
  1362. long tenex_parse (MAILSTREAM *stream)
  1363. {
  1364.   struct stat sbuf;
  1365.   MESSAGECACHE *elt = NIL;
  1366.   char c,*s,*t,*x;
  1367.   char tmp[MAILTMPLEN];
  1368.   unsigned long i,j,msiz;
  1369.   long curpos = LOCAL->filesize;
  1370.   long nmsgs = stream->nmsgs;
  1371.   long recent = stream->recent;
  1372.   fstat (LOCAL->fd,&sbuf);    /* get status */
  1373.   if (sbuf.st_size < curpos) {    /* sanity check */
  1374.     sprintf (tmp,"Mailbox shrank from %ld to %ld!",curpos,sbuf.st_size);
  1375.     mm_log (tmp,ERROR);
  1376.     tenex_close (stream);
  1377.     return NIL;
  1378.   }
  1379.   while (sbuf.st_size - curpos){/* while there is stuff to parse */
  1380.                 /* get to that position in the file */
  1381.     lseek (LOCAL->fd,curpos,L_SET);
  1382.     if ((i = read (LOCAL->fd,LOCAL->buf,64)) <= 0) {
  1383.       sprintf (tmp,"Unable to read internal header at %ld, size = %ld: %s",
  1384.            curpos,sbuf.st_size,i ? strerror (errno) : "no data read");
  1385.       mm_log (tmp,ERROR);
  1386.       tenex_close (stream);
  1387.       return NIL;
  1388.     }
  1389.     LOCAL->buf[i] = '\0';    /* tie off buffer just in case */
  1390.     if (!(s = strchr (LOCAL->buf,'\012'))) {
  1391.       sprintf (tmp,"Unable to find newline at %ld in %ld bytes, text: %s",
  1392.            curpos,i,LOCAL->buf);
  1393.       mm_log (tmp,ERROR);
  1394.       tenex_close (stream);
  1395.       return NIL;
  1396.     }
  1397.     *s = '\0';            /* tie off header line */
  1398.     i = (s + 1) - LOCAL->buf;    /* note start of text offset */
  1399.     if (!((s = strchr (LOCAL->buf,',')) && (t = strchr (s+1,';')))) {
  1400.       sprintf (tmp,"Unable to parse internal header at %ld: %s",curpos,
  1401.            LOCAL->buf);
  1402.       mm_log (tmp,ERROR);
  1403.       tenex_close (stream);
  1404.       return NIL;
  1405.     }
  1406.     *s++ = '\0'; *t++ = '\0';    /* tie off fields */
  1407.  
  1408.                 /* intantiate an elt for this message */
  1409.     (elt = mail_elt (stream,++nmsgs))->valid = T;
  1410.     elt->data1 = curpos;    /* note file offset of header */
  1411.     elt->data2 = i << 24;    /* as well as offset from header of message */
  1412.                 /* parse the header components */
  1413.     if (!(mail_parse_date (elt,LOCAL->buf) &&
  1414.       (msiz = strtol (x = s,&s,10)) && (!(s && *s)) &&
  1415.       isdigit (t[0]) && isdigit (t[1]) && isdigit (t[2]) &&
  1416.       isdigit (t[3]) && isdigit (t[4]) && isdigit (t[5]) &&
  1417.       isdigit (t[6]) && isdigit (t[7]) && isdigit (t[8]) &&
  1418.       isdigit (t[9]) && isdigit (t[10]) && isdigit (t[11]) && !t[12])) {
  1419.       sprintf (tmp,"Unable to parse internal header elements at %ld: %s,%s;%s",
  1420.            curpos,LOCAL->buf,x,t);
  1421.       mm_log (tmp,ERROR);
  1422.       tenex_close (stream);
  1423.       return NIL;
  1424.     }
  1425.                 /* make sure didn't run off end of file */
  1426.     if ((curpos += (msiz + i)) > sbuf.st_size) {
  1427.       mm_log ("Last message runs past end of file",ERROR);
  1428.       tenex_close (stream);
  1429.       return NIL;
  1430.     }
  1431.     c = t[10];            /* remember first system flags byte */
  1432.     t[10] = '\0';        /* tie off flags */
  1433.     j = strtol (t,NIL,8);    /* get user flags value */
  1434.     t[10] = c;            /* restore first system flags byte */
  1435.                 /* set up all valid user flags (reversed!) */
  1436.     while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) &&
  1437.           stream->user_flags[i]) elt->user_flags |= 1 << i;
  1438.                 /* calculate system flags */
  1439.     if ((j = ((t[10]-'0') * 8) + t[11]-'0') & fSEEN) elt->seen = T;
  1440.     if (j & fDELETED) elt->deleted = T;
  1441.     if (j & fFLAGGED) elt->flagged = T;
  1442.     if (j & fANSWERED) elt->answered = T;
  1443.     if (!(j & fOLD)) {        /* newly arrived message? */
  1444.       elt->recent = T;
  1445.       recent++;            /* count up a new recent message */
  1446.                 /* mark it as old */
  1447.       tenex_update_status (stream,nmsgs,NIL);
  1448.     }
  1449.   }
  1450.   fsync (LOCAL->fd);        /* make sure all the fOLD flags take */
  1451.                 /* update parsed file size and time */
  1452.   LOCAL->filesize = sbuf.st_size;
  1453.   fstat (LOCAL->fd,&sbuf);    /* get status again to ensure time is right */
  1454.   LOCAL->filetime = sbuf.st_mtime;
  1455.   mail_exists (stream,nmsgs);    /* notify upper level of new mailbox size */
  1456.   mail_recent (stream,recent);    /* and of change in recent messages */
  1457.   return LONGT;            /* return the winnage */
  1458. }
  1459.  
  1460. /* Tenex copy messages
  1461.  * Accepts: MAIL stream
  1462.  *        mailbox copy vector
  1463.  *        mailbox name
  1464.  * Returns: T if success, NIL if failed
  1465.  */
  1466.  
  1467. long tenex_copy_messages (MAILSTREAM *stream,char *mailbox)
  1468. {
  1469.   struct stat sbuf;
  1470.   time_t tp[2];
  1471.   MESSAGECACHE *elt;
  1472.   unsigned long i,j,k;
  1473.   long ret = LONGT;
  1474.   int fd,ld;
  1475.   char file[MAILTMPLEN],lock[MAILTMPLEN];
  1476.                 /* make sure valid mailbox */
  1477.   if (!tenex_isvalid (mailbox,LOCAL->buf)) switch (errno) {
  1478.   case ENOENT:            /* no such file? */
  1479.     mm_notify (stream,"[TRYCREATE] Must create mailbox before copy",NIL);
  1480.     return NIL;
  1481.   case 0:            /* merely empty file? */
  1482.     break;
  1483.   case EINVAL:
  1484.     sprintf (LOCAL->buf,"Invalid Tenex-format mailbox name: %s",mailbox);
  1485.     mm_log (LOCAL->buf,ERROR);
  1486.     return NIL;
  1487.   default:
  1488.     sprintf (LOCAL->buf,"Not a Tenex-format mailbox: %s",mailbox);
  1489.     mm_log (LOCAL->buf,ERROR);
  1490.     return NIL;
  1491.   }
  1492.                 /* got file? */  
  1493.   if ((fd=open(tenex_file(file,mailbox),O_RDWR|O_CREAT,S_IREAD|S_IWRITE))<0) {
  1494.     sprintf (LOCAL->buf,"Unable to open copy mailbox: %s",strerror (errno));
  1495.     mm_log (LOCAL->buf,ERROR);
  1496.     return NIL;
  1497.   }
  1498.   mm_critical (stream);        /* go critical */
  1499.                 /* get exclusive parse/append permission */
  1500.   if ((ld = tenex_lock (fd,lock,LOCK_EX)) < 0) {
  1501.     mm_log ("Unable to lock copy mailbox",ERROR);
  1502.     return NIL;
  1503.   }
  1504.   fstat (fd,&sbuf);        /* get current file size */
  1505.   lseek (fd,sbuf.st_size,L_SET);/* move to end of file */
  1506.  
  1507.                 /* for each requested message */
  1508.   for (i = 1; ret && (i <= stream->nmsgs); i++) 
  1509.     if ((elt = mail_elt (stream,i))->sequence) {
  1510.       lseek (LOCAL->fd,elt->data1,L_SET);
  1511.                 /* number of bytes to copy */
  1512.       k = (elt->data2 >> 24) + tenex_size (stream,i);
  1513.       do {            /* read from source position */
  1514.     j = min (k,LOCAL->buflen);
  1515.     read (LOCAL->fd,LOCAL->buf,(unsigned int) j);
  1516.     if ((write (fd,LOCAL->buf,(unsigned int) j) < 0) || fsync (fd)) {
  1517.       sprintf (LOCAL->buf,"Unable to write message: %s",strerror (errno));
  1518.       mm_log (LOCAL->buf,ERROR);
  1519.       ftruncate (fd,sbuf.st_size);
  1520.       j = k;
  1521.       ret = NIL;        /* note error */
  1522.       break;
  1523.     }
  1524.       } while (k -= j);        /* until done */
  1525.     }
  1526.   tp[0] = sbuf.st_atime;    /* preserve atime and mtime */
  1527.   tp[1] = sbuf.st_mtime;
  1528.   utime (file,tp);        /* set the times */
  1529.   tenex_unlock (ld,lock);    /* release exclusive parse/append permission */
  1530.   close (fd);            /* close the file */
  1531.   mm_nocritical (stream);    /* release critical */
  1532.   return ret;
  1533. }
  1534.  
  1535. /* Tenex get cache element with status updating from file
  1536.  * Accepts: MAIL stream
  1537.  *        message number
  1538.  * Returns: cache element
  1539.  */
  1540.  
  1541. MESSAGECACHE *tenex_elt (MAILSTREAM *stream,long msgno)
  1542. {
  1543.   unsigned long i,j,sysflags;
  1544.   char c;
  1545.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1546.   unsigned long oldsysflags = (elt->seen ? fSEEN : NIL) |
  1547.     (elt->deleted ? fDELETED : NIL) | (elt->flagged ? fFLAGGED : NIL) |
  1548.       (elt->answered ? fANSWERED : NIL);
  1549.   unsigned long olduserflags = elt->user_flags;
  1550.                 /* set the seek pointer */
  1551.   lseek (LOCAL->fd,(off_t) elt->data1 + (elt->data2 >> 24) - 13,L_SET);
  1552.                 /* read the new flags */
  1553.   if (read (LOCAL->fd,LOCAL->buf,12) < 0) {
  1554.     sprintf (LOCAL->buf,"Unable to read new status: %s",strerror (errno));
  1555.     fatal (LOCAL->buf);
  1556.   }
  1557.                 /* calculate system flags */
  1558.   sysflags = (((LOCAL->buf[10]-'0') * 8) + LOCAL->buf[11]-'0') &
  1559.     (fSEEN|fDELETED|fFLAGGED|fANSWERED);
  1560.   elt->seen = sysflags & fSEEN ? T : NIL;
  1561.   elt->deleted = sysflags & fDELETED ? T : NIL;
  1562.   elt->flagged = sysflags & fFLAGGED ? T : NIL;
  1563.   elt->answered = sysflags & fANSWERED ? T : NIL;
  1564.   c = LOCAL->buf[10];        /* remember first system flags byte */
  1565.   LOCAL->buf[10] = '\0';    /* tie off flags */
  1566.   j = strtol (LOCAL->buf,NIL,8);/* get user flags value */
  1567.   LOCAL->buf[10] = c;        /* restore first system flags byte */
  1568.                 /* set up all valid user flags (reversed!) */
  1569.   while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) &&
  1570.         stream->user_flags[i]) elt->user_flags |= 1 << i;
  1571.   if ((oldsysflags != sysflags) || (olduserflags != elt->user_flags))
  1572.     mm_flags (stream,msgno);    /* let top level know */
  1573.   return elt;
  1574. }
  1575.  
  1576. /* Tenex update status string
  1577.  * Accepts: MAIL stream
  1578.  *        message number
  1579.  *        flag saying whether or not to sync
  1580.  */
  1581.  
  1582. void tenex_update_status (MAILSTREAM *stream,long msgno,long syncflag)
  1583. {
  1584.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1585.   struct stat sbuf;
  1586.   unsigned long j,k = 0;
  1587.   if (!stream->rdonly) {    /* not if readonly you don't */
  1588.     j = elt->user_flags;    /* get user flags */
  1589.                 /* reverse bits (dontcha wish we had CIRC?) */
  1590.     while (j) k |= 1 << (29 - find_rightmost_bit (&j));
  1591.                 /* print new flag string */
  1592.     sprintf (LOCAL->buf,"%010lo%02o",k,
  1593.          fOLD + (fSEEN * elt->seen) + (fDELETED * elt->deleted) +
  1594.          (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered));
  1595.                 /* get to that place in the file */
  1596.     lseek (LOCAL->fd,(off_t) elt->data1 + (elt->data2 >> 24) - 13,L_SET);
  1597.                 /* write new flags */
  1598.     write (LOCAL->fd,LOCAL->buf,12);
  1599.                 /* sync if requested */
  1600.     if (syncflag) fsync (LOCAL->fd);
  1601.     fstat (LOCAL->fd,&sbuf);    /* get new write time */
  1602.     LOCAL->filetime = sbuf.st_mtime;
  1603.   }
  1604. }
  1605.  
  1606. /* Search support routines
  1607.  * Accepts: MAIL stream
  1608.  *        message number
  1609.  *        pointer to additional data
  1610.  * Returns: T if search matches, else NIL
  1611.  */
  1612.  
  1613.  
  1614. char tenex_search_all (MAILSTREAM *stream,long msgno,char *d,long n)
  1615. {
  1616.   return T;            /* ALL always succeeds */
  1617. }
  1618.  
  1619.  
  1620. char tenex_search_answered (MAILSTREAM *stream,long msgno,char *d,long n)
  1621. {
  1622.   return mail_elt (stream,msgno)->answered ? T : NIL;
  1623. }
  1624.  
  1625.  
  1626. char tenex_search_deleted (MAILSTREAM *stream,long msgno,char *d,long n)
  1627. {
  1628.   return mail_elt (stream,msgno)->deleted ? T : NIL;
  1629. }
  1630.  
  1631.  
  1632. char tenex_search_flagged (MAILSTREAM *stream,long msgno,char *d,long n)
  1633. {
  1634.   return mail_elt (stream,msgno)->flagged ? T : NIL;
  1635. }
  1636.  
  1637.  
  1638. char tenex_search_keyword (MAILSTREAM *stream,long msgno,char *d,long n)
  1639. {
  1640.   return mail_elt (stream,msgno)->user_flags & n ? T : NIL;
  1641. }
  1642.  
  1643.  
  1644. char tenex_search_new (MAILSTREAM *stream,long msgno,char *d,long n)
  1645. {
  1646.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1647.   return (elt->recent && !elt->seen) ? T : NIL;
  1648. }
  1649.  
  1650. char tenex_search_old (MAILSTREAM *stream,long msgno,char *d,long n)
  1651. {
  1652.   return mail_elt (stream,msgno)->recent ? NIL : T;
  1653. }
  1654.  
  1655.  
  1656. char tenex_search_recent (MAILSTREAM *stream,long msgno,char *d,long n)
  1657. {
  1658.   return mail_elt (stream,msgno)->recent ? T : NIL;
  1659. }
  1660.  
  1661.  
  1662. char tenex_search_seen (MAILSTREAM *stream,long msgno,char *d,long n)
  1663. {
  1664.   return mail_elt (stream,msgno)->seen ? T : NIL;
  1665. }
  1666.  
  1667.  
  1668. char tenex_search_unanswered (MAILSTREAM *stream,long msgno,char *d,long n)
  1669. {
  1670.   return mail_elt (stream,msgno)->answered ? NIL : T;
  1671. }
  1672.  
  1673.  
  1674. char tenex_search_undeleted (MAILSTREAM *stream,long msgno,char *d,long n)
  1675. {
  1676.   return mail_elt (stream,msgno)->deleted ? NIL : T;
  1677. }
  1678.  
  1679.  
  1680. char tenex_search_unflagged (MAILSTREAM *stream,long msgno,char *d,long n)
  1681. {
  1682.   return mail_elt (stream,msgno)->flagged ? NIL : T;
  1683. }
  1684.  
  1685.  
  1686. char tenex_search_unkeyword (MAILSTREAM *stream,long msgno,char *d,long n)
  1687. {
  1688.   return mail_elt (stream,msgno)->user_flags & n ? NIL : T;
  1689. }
  1690.  
  1691.  
  1692. char tenex_search_unseen (MAILSTREAM *stream,long msgno,char *d,long n)
  1693. {
  1694.   return mail_elt (stream,msgno)->seen ? NIL : T;
  1695. }
  1696.  
  1697. char tenex_search_before (MAILSTREAM *stream,long msgno,char *d,long n)
  1698. {
  1699.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1700.   return (char) ((long)((elt->year << 9) + (elt->month << 5) + elt->day) < n);
  1701. }
  1702.  
  1703.  
  1704. char tenex_search_on (MAILSTREAM *stream,long msgno,char *d,long n)
  1705. {
  1706.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1707.   return (char) (((elt->year << 9) + (elt->month << 5) + elt->day) == n);
  1708. }
  1709.  
  1710.  
  1711. char tenex_search_since (MAILSTREAM *stream,long msgno,char *d,long n)
  1712. {
  1713.                 /* everybody interprets "since" as .GE. */
  1714.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1715.   return (char) ((long)((elt->year << 9) + (elt->month << 5) + elt->day) >= n);
  1716. }
  1717.  
  1718.  
  1719. char tenex_search_body (MAILSTREAM *stream,long msgno,char *d,long n)
  1720. {
  1721.   unsigned long textsize;
  1722.   char *s = tenex_fetchtext_work (stream,msgno,&textsize);
  1723.                 /* recalculate status */
  1724.   tenex_update_status (stream,msgno,T);
  1725.   return search (s,textsize,d,n);
  1726. }
  1727.  
  1728.  
  1729. char tenex_search_subject (MAILSTREAM *stream,long msgno,char *d,long n)
  1730. {
  1731.   char *s = tenex_fetchstructure (stream,msgno,NIL)->subject;
  1732.   return s ? search (s,(long) strlen (s),d,n) : NIL;
  1733. }
  1734.  
  1735.  
  1736. char tenex_search_text (MAILSTREAM *stream,long msgno,char *d,long n)
  1737. {
  1738.   unsigned long hdrsize;
  1739.   char *s = tenex_fetchheader_work (stream,msgno,&hdrsize);
  1740.   return search (s,hdrsize,d,n) || tenex_search_body (stream,msgno,d,n);
  1741. }
  1742.  
  1743. char tenex_search_bcc (MAILSTREAM *stream,long msgno,char *d,long n)
  1744. {
  1745.   ADDRESS *a = tenex_fetchstructure (stream,msgno,NIL)->bcc;
  1746.   LOCAL->buf[0] = '\0';        /* initially empty string */
  1747.                 /* get text for address */
  1748.   rfc822_write_address (LOCAL->buf,a);
  1749.   return search (LOCAL->buf,(long) strlen (LOCAL->buf),d,n);
  1750. }
  1751.  
  1752.  
  1753. char tenex_search_cc (MAILSTREAM *stream,long msgno,char *d,long n)
  1754. {
  1755.   ADDRESS *a = tenex_fetchstructure (stream,msgno,NIL)->cc;
  1756.   LOCAL->buf[0] = '\0';        /* initially empty string */
  1757.                 /* get text for address */
  1758.   rfc822_write_address (LOCAL->buf,a);
  1759.   return search (LOCAL->buf,(long) strlen (LOCAL->buf),d,n);
  1760. }
  1761.  
  1762.  
  1763. char tenex_search_from (MAILSTREAM *stream,long msgno,char *d,long n)
  1764. {
  1765.   ADDRESS *a = tenex_fetchstructure (stream,msgno,NIL)->from;
  1766.   LOCAL->buf[0] = '\0';        /* initially empty string */
  1767.                 /* get text for address */
  1768.   rfc822_write_address (LOCAL->buf,a);
  1769.   return search (LOCAL->buf,(long) strlen (LOCAL->buf),d,n);
  1770. }
  1771.  
  1772.  
  1773. char tenex_search_to (MAILSTREAM *stream,long msgno,char *d,long n)
  1774. {
  1775.   ADDRESS *a = tenex_fetchstructure (stream,msgno,NIL)->to;
  1776.   LOCAL->buf[0] = '\0';        /* initially empty string */
  1777.                 /* get text for address */
  1778.   rfc822_write_address (LOCAL->buf,a);
  1779.   return search (LOCAL->buf,(long) strlen (LOCAL->buf),d,n);
  1780. }
  1781.  
  1782. /* Search parsers */
  1783.  
  1784.  
  1785. /* Parse a date
  1786.  * Accepts: function to return
  1787.  *        pointer to date integer to return
  1788.  * Returns: function to return
  1789.  */
  1790.  
  1791. search_t tenex_search_date (search_t f,long *n)
  1792. {
  1793.   long i;
  1794.   char *s;
  1795.   MESSAGECACHE elt;
  1796.                 /* parse the date and return fn if OK */
  1797.   return (tenex_search_string (f,&s,&i) && mail_parse_date (&elt,s) &&
  1798.       (*n = (elt.year << 9) + (elt.month << 5) + elt.day)) ? f : NIL;
  1799. }
  1800.  
  1801. /* Parse a flag
  1802.  * Accepts: function to return
  1803.  *        pointer to keyword integer to return
  1804.  *        MAIL stream
  1805.  * Returns: function to return
  1806.  */
  1807.  
  1808. search_t tenex_search_flag (search_t f,long *n,MAILSTREAM *stream)
  1809. {
  1810.   short i;
  1811.   char *s,*t;
  1812.   if (t = strtok (NIL," ")) {    /* get a keyword */
  1813.     ucase (t);            /* get uppercase form of flag */
  1814.     for (i = 0; i < NUSERFLAGS && (s = stream->user_flags[i]); ++i)
  1815.       if (!strcmp (t,ucase (strcpy (LOCAL->buf,s))) && (*n = 1 << i)) return f;
  1816.   }
  1817.   return NIL;            /* couldn't find keyword */
  1818. }
  1819.  
  1820. /* Parse a string
  1821.  * Accepts: function to return
  1822.  *        pointer to string to return
  1823.  *        pointer to string length to return
  1824.  * Returns: function to return
  1825.  */
  1826.  
  1827.  
  1828. search_t tenex_search_string (search_t f,char **d,long *n)
  1829. {
  1830.   char *end = " ";
  1831.   char *c = strtok (NIL,"");    /* remainder of criteria */
  1832.   if (!c) return NIL;        /* missing argument */
  1833.   switch (*c) {            /* see what the argument is */
  1834.   case '{':            /* literal string */
  1835.     *n = strtol (c+1,d,10);    /* get its length */
  1836.     if ((*(*d)++ == '}') && (*(*d)++ == '\015') && (*(*d)++ == '\012') &&
  1837.     (!(*(c = *d + *n)) || (*c == ' '))) {
  1838.       char e = *--c;
  1839.       *c = DELIM;        /* make sure not a space */
  1840.       strtok (c," ");        /* reset the strtok mechanism */
  1841.       *c = e;            /* put character back */
  1842.       break;
  1843.     }
  1844.   case '\0':            /* catch bogons */
  1845.   case ' ':
  1846.     return NIL;
  1847.   case '"':            /* quoted string */
  1848.     if (strchr (c+1,'"')) end = "\"";
  1849.     else return NIL;
  1850.   default:            /* atomic string */
  1851.     if (*d = strtok (c,end)) *n = strlen (*d);
  1852.     else return NIL;
  1853.     break;
  1854.   }
  1855.   return f;
  1856. }
  1857.